#include "alg.h"
#include <QString>
#include <QtDebug>
#include "sm3.h"
#include "sm4.h"
#include "des.h"
#include <stdexcept>

// namespace SDHuaYi_Alg
//{

QString HYALG_SM3Hash(QString indata)
{
    uint8_t outdata[32] = {0};
    if (indata.isEmpty())
    {
        throw std::runtime_error("sm3 indata err");
    }
    QByteArray byteIndata = QByteArray::fromHex(indata.toUtf8());
    uint8_t *pindata = reinterpret_cast<uint8_t *>(byteIndata.data());
    uint32_t indataLen = static_cast<uint32_t>(indata.count() / 2);

    soft_sm3(pindata, indataLen, outdata);
    QString retStr;
    for (uint32_t i = 0; i < 32; i++)
    {
        retStr.append(QString("%1").arg(outdata[i], 2, 16, QChar('0')).toUpper());
    }

    return retStr;
}

QString HYALG_SM4Crypt(enumAlgMode mode, enumAlgEncOrDec type, QString key, QString iv, QString indata)
{
//    uint8_t outdata[indata.count() / 2];
    uint32_t outdataLen;
    uint32_t encOrDec;

    // KEY
    if (key.isEmpty())
    {
        throw std::runtime_error("sm4 key err");
    }
    QByteArray byteKey = QByteArray::fromHex(key.toUtf8());
    uint8_t *pkey = reinterpret_cast<uint8_t *>(byteKey.data());
    //    uint32_t keyLen = static_cast<uint32_t>(key.count() / 2);

    // CRYPT
    switch (mode)
    {
    case ALG_MODE_ECB:
    {
        // INDATA
        if (indata.isEmpty())
        {
            throw std::runtime_error("sm4 indata err");
        }
        QByteArray byteIndata = QByteArray::fromHex(indata.toUtf8());
        uint8_t *pindata = reinterpret_cast<uint8_t *>(byteIndata.data());
        uint32_t indataLen = static_cast<uint32_t>(indata.count() / 2);

        QString retStr;
        if (type == ALG_ENC)
            encOrDec = SM4_ENCRYPT;
        else if (type == ALG_DEC)
            encOrDec = SM4_DECRYPT;
        else
            throw std::runtime_error("不支持的算法执行");

        size_t size = static_cast<size_t>(indata.count())/2;
        uint8_t *outdata = static_cast<uint8_t*>(malloc(size));
        // void sm4_ecb_encrypt(const U8 *in, U8 *out, const U32 length, const U8 *key, const U32 enc)
        sm4_ecb_encrypt(pindata, outdata, indataLen, pkey, encOrDec);
        outdataLen = indataLen;
        for (uint32_t i = 0; i < outdataLen; i++)
        {
            retStr.append(QString("%1").arg(outdata[i], 2, 16, QChar('0')).toUpper());
        }
        free(outdata);

        return retStr;
    }
    break;
    case ALG_MODE_CBC:
    {
        // INDATA
        if (indata.isEmpty())
        {
            throw std::runtime_error("sm4 indata err");
        }
        QByteArray byteIndata = QByteArray::fromHex(indata.toUtf8());
        uint8_t *pindata = reinterpret_cast<uint8_t *>(byteIndata.data());
        uint32_t indataLen = static_cast<uint32_t>(indata.count() / 2);

        QString retStr;
        if (type == ALG_ENC)
            encOrDec = SM4_ENCRYPT;
        else if (type == ALG_DEC)
            encOrDec = SM4_DECRYPT;
        else
            throw std::runtime_error("不支持的算法执行");

        // IV
        if (iv.isEmpty())
        {
            throw std::runtime_error("iv data err");
        }
        QByteArray byteIv = QByteArray::fromHex(iv.toUtf8());
        uint8_t *piv = reinterpret_cast<uint8_t *>(byteIv.data());

        size_t size = static_cast<size_t>(indata.count())/2;
        uint8_t *outdata = static_cast<uint8_t*>(malloc(size));

        // void sm4_cbc_encrypt(const U8 *in, U8 *out, const U32 length, const U8 *key, const U8 *ivec, const U32 enc);
        sm4_cbc_encrypt(pindata, outdata, indataLen, pkey, piv, encOrDec);
        outdataLen = indataLen;
        for (uint32_t i = 0; i < outdataLen; i++)
        {
            retStr.append(QString("%1").arg(outdata[i], 2, 16, QChar('0')).toUpper());
        }
        free(outdata);

        return retStr;
    }

    break;
    case ALG_MODE_MAC:
    {
        QString retStr;

        // INDATA 填充80
        if (indata.isEmpty())
        {
            throw std::runtime_error("sm4 indata err");
        }
        if (indata.count() % 32 == 0)
        {
            indata = indata + "80000000000000000000000000000000";
        }
        else
        {
            int len = 32 - (indata.count() % 32);
            indata = indata + "80";
            for (int i = 0; i < len - 2; i++)
            {
                indata += "0";
            }
        }
        QByteArray byteIndata = QByteArray::fromHex(indata.toUtf8());
        uint8_t *pindata = reinterpret_cast<uint8_t *>(byteIndata.data());
        uint32_t indataLen = static_cast<uint32_t>(indata.count() / 2);

        // IV
        if (iv.isEmpty())
        {
            throw std::runtime_error("iv data err");
        }
        QByteArray byteIv = QByteArray::fromHex(iv.toUtf8());
        uint8_t *piv = reinterpret_cast<uint8_t *>(byteIv.data());

        size_t size = static_cast<size_t>(indata.count())/2;
        uint8_t *outdata = static_cast<uint8_t*>(malloc(size));
        // void sm4_cbc_mac(const U8 *in, U8 *out, const U32 length, const U8 *key, const U8 *ivec)
        sm4_cbc_mac(pindata, outdata, indataLen, pkey, piv);
        outdataLen = 16;
        for (uint32_t i = 0; i < outdataLen; i++)
        {
            retStr.append(QString("%1").arg(outdata[i], 2, 16, QChar('0')).toUpper());
        }
        free(outdata);

        return retStr;
    }
    break;
    default:
        throw std::runtime_error("不支持的算法模式");
        break;
    }
    throw std::runtime_error("程序不会运行到这儿");
}

QString HYALG_DESCrypt(enumAlgMode mode, enumAlgEncOrDec type, QString key, QString iv, QString indata)
{
//    uint8_t outdata[indata.count() / 2];
    uint32_t outdataLen;
    uint32_t encOrDec;

    // KEY
    if (key.isEmpty())
    {
        throw std::runtime_error("sm4 key err");
    }
    QByteArray byteKey = QByteArray::fromHex(key.toUtf8());
    uint8_t *pkey = reinterpret_cast<uint8_t *>(byteKey.data());
    uint32_t keyLen = static_cast<uint32_t>(key.count() / 2);

    // INDATA
    if (indata.isEmpty())
    {
        throw std::runtime_error("sm4 indata err");
    }
    QByteArray byteIndata = QByteArray::fromHex(indata.toUtf8());
    uint8_t *pindata = reinterpret_cast<uint8_t *>(byteIndata.data());
    uint32_t indataLen = static_cast<uint32_t>(indata.count() / 2);

    switch (mode)
    {
    case ALG_MODE_ECB:
    {
        QString retStr;
        if (type == ALG_ENC)
            encOrDec = ALG_ENC;
        else if (type == ALG_DEC)
            encOrDec = ALG_DEC;
        else
            throw std::runtime_error("不支持的算法执行");

        size_t size = static_cast<size_t>(indata.count())/2;
        uint8_t *outdata = static_cast<uint8_t*>(malloc(size));
        // extern int des_ecb_run(int flag, unsigned char *pKey, unsigned int KeyLen, unsigned char *pInData,
        //                    unsigned int inDataLen, unsigned char *pOutData, unsigned int *pOutDataLen);
        des_ecb_run(encOrDec, pkey, keyLen, pindata, indataLen, outdata, &outdataLen);
        for (uint32_t i = 0; i < outdataLen; i++)
        {
            retStr.append(QString("%1").arg(outdata[i], 2, 16, QChar('0')).toUpper());
        }
        free(outdata);

        return retStr;
    }
    break;
    case ALG_MODE_CBC:
    {
        QString retStr;
        if (type == ALG_ENC)
            encOrDec = ALG_ENC;
        else if (type == ALG_DEC)
            encOrDec = ALG_DEC;
        else
            throw std::runtime_error("不支持的算法执行");

        // IV
        if (iv.isEmpty())
        {
            throw std::runtime_error("iv data err");
        }
        QByteArray byteIv = QByteArray::fromHex(iv.toUtf8());
        uint8_t *piv = reinterpret_cast<uint8_t *>(byteIv.data());

        size_t size = static_cast<size_t>(indata.count())/2;
        uint8_t *outdata = static_cast<uint8_t*>(malloc(size));
        // extern int des_cbc_run(int flag, unsigned char *pKey, unsigned int KeyLen, unsigned char *pInData,
        //                        unsigned int inDataLen, unsigned char *pOutData, unsigned int *pOutDataLen,
        //                        unsigned char *iv);
        des_cbc_run(encOrDec, pkey, keyLen, pindata, indataLen, outdata, &outdataLen, piv);
        for (uint32_t i = 0; i < outdataLen; i++)
        {
            retStr.append(QString("%1").arg(outdata[i], 2, 16, QChar('0')).toUpper());
        }
        free(outdata);

        return retStr;
    }
    break;
    case ALG_MODE_MAC:
       {
        QString retStr;

        // INDATA 填充80
        if (indata.isEmpty())
        {
            throw std::runtime_error("des indata err");
        }
        if (indata.count() % 16 == 0)
        {
            indata = indata + "8000000000000000";
        }
        else
        {
            int len = 16 - (indata.count() % 16);
            indata = indata + "80";
            for (int i = 0; i < len - 2; i++)
            {
                indata += "0";
            }
        }
        QByteArray byteIndata = QByteArray::fromHex(indata.toUtf8());
        uint8_t *pindata = reinterpret_cast<uint8_t *>(byteIndata.data());
        uint32_t indataLen = static_cast<uint32_t>(indata.count() / 2);

        // IV
        if (iv.isEmpty())
        {
            throw std::runtime_error("iv data err");
        }
        QByteArray byteIv = QByteArray::fromHex(iv.toUtf8());
        uint8_t *piv = reinterpret_cast<uint8_t *>(byteIv.data());

        size_t size = static_cast<size_t>(indata.count())/2;
        uint8_t *outdata = static_cast<uint8_t*>(malloc(size));
        des_cbc_mac(pindata,outdata,indataLen,pkey,keyLen,piv);
        outdataLen = 8;
        for (uint32_t i = 0; i < outdataLen; i++)
        {
            retStr.append(QString("%1").arg(outdata[i], 2, 16, QChar('0')).toUpper());
        }
        free(outdata);

        return retStr;
    }
//    break;
    default:
        throw std::runtime_error("不支持的算法模式");
//        break;
    }
    throw std::runtime_error("程序不会运行到这儿");
}

